﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using System.Collections.ObjectModel;

namespace CoolEngine.Engine.Graphics
{
    public class Bone
    {
        public Bone Parent { get; private set; }
        private Matrix transform = Matrix.Identity;
        public Matrix Transform { get { return transform; } set { transform = value; constructedTransform = false; } }
        private readonly List<Bone> children = new List<Bone>();
        private readonly ReadOnlyCollection<Bone> children_readonly;
        public ReadOnlyCollection<Bone> Children { get { return children_readonly; } }
        private Matrix accumulatedTransform;
        private bool constructedTransform;
        public Bone(Bone parent)
        {
            Parent = parent;
            children_readonly = new ReadOnlyCollection<Bone>(children);
            parent.children.Add(this);
        }
        public Matrix GetAccumulatedTransform()
        {
            if (constructedTransform)
            {
                return accumulatedTransform;
            }
            if (Parent != null)
            {
                accumulatedTransform = Parent.GetAccumulatedTransform() * Transform;
                constructedTransform = true;
            }
            else
            {
                accumulatedTransform = Transform;
                constructedTransform = true;
            }
            return accumulatedTransform;
        }
        public void ResetTransform()
        {
            constructedTransform = false;
        }
        private void ApplyTransform(ref Matrix matrix)
        {
            transform *= matrix;
            ResetTransform();
        }
        public void Rotate(Quaternion q)
        {
            Matrix m;
            Matrix.CreateFromQuaternion(ref q, out m);
            ApplyTransform(ref m);
        }
        public void Rotate(Vector3 axis, float angle)
        {
            Matrix m;
            Matrix.CreateFromAxisAngle(ref axis, angle, out m);
            ApplyTransform(ref m);
        }
        public void Rotate(Matrix matrix)
        {
            Rotate(ref matrix);
        }

        private void Rotate(ref Matrix matrix)
        {
            ApplyTransform(ref matrix);
        }
        public void RotateX(float angle)
        {
            Matrix m;
            Matrix.CreateRotationX(angle, out m);
            ApplyTransform(ref m);
        }
        public void RotateY(float angle)
        {
            Matrix m;
            Matrix.CreateRotationY(angle, out m);
            ApplyTransform(ref m);
        }
        public void RotateZ(float angle)
        {
            Matrix m;
            Matrix.CreateRotationZ(angle, out m);
            ApplyTransform(ref m);
        }
        public Vector3 Position
        {
            get
            {
                return transform.Translation;
            }
            set
            {
                transform.Translation = value;
                ResetTransform();
            }
        }
        public Quaternion Rotation
        {
            get
            {
                return Quaternion.CreateFromRotationMatrix(transform);
            }
            set
            {
                Matrix m;
                Matrix.CreateFromQuaternion(ref value, out m);

                transform.M11 = m.M11;
                transform.M12 = m.M12;
                transform.M13 = m.M13;
                transform.M21 = m.M21;
                transform.M22 = m.M22;
                transform.M23 = m.M23;
                transform.M31 = m.M31;
                transform.M32 = m.M32;
                transform.M33 = m.M33;
                ResetTransform();
            }
        }
    }
}
